fix(DX-10060): retry transient network errors to prevent build crashes - #385
Conversation
Transient network-layer errors (ENOTFOUND, ENETUNREACH, ECONNRESET, ECONNREFUSED, EAI_AGAIN, ETIMEDOUT, EHOSTUNREACH, ENETDOWN) now trigger the SDK's configured retry policy instead of failing immediately. A combinedRetryCondition composes the user-supplied retryCondition with the new default network-error check. The user condition runs first; if it throws, a warning is emitted via logHandler and the SDK falls back to the default. The original config object is never mutated. ECONNABORTED is excluded — @contentstack/core classifies it as a structured TIMEOUT error and handles it separately. Resolves: SF Case #00060601 (SentinelOne) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Coverage report
Test suite run success720 tests passing in 36 suites. Report generated by 🧪jest coverage report action from 3e4e178 |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…warnings Lines 182 and 184 in contentstack.ts (the catch block and logHandler warn path) were flagged uncovered by jest-coverage-report-action. Added test (g) which exercises a retryCondition that throws, verifies the SDK falls back to default retry behaviour, and asserts the warning is emitted via logHandler. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Not ready to approve
There are correctness and consistency issues in the new retry helper/documentation and logging payload that should be addressed before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
Adds default retry behavior for transient network-layer failures in the Delivery SDK by composing a new network-error retry check with any user-provided retryCondition, and documents/releases the change as a minor version bump.
Changes:
- Composes a
combinedRetryConditionin the stack client to retry common transient network error codes while preserving the original userretryConditionreference. - Adds a utility helper (
isTransientNetworkError) + constant set of retryable network error codes. - Introduces unit tests covering the new retry behavior and updates version/changelog to 5.6.0.
File summaries
| File | Description |
|---|---|
| test/unit/network-error-retry.spec.ts | Adds unit tests validating retries for specific transient network error codes and opt-out behaviors. |
| src/stack/contentstack.ts | Composes retry behavior with user retryCondition and adds warning logging when user logic throws. |
| src/common/utils.ts | Adds transient network error code set and helper used by the new retry logic. |
| package.json | Bumps SDK version to 5.6.0. |
| CHANGELOG.md | Documents the retry behavior change and release date for 5.6.0. |
Review details
- Files reviewed: 5/5 changed files
- Comments generated: 3
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
🔒 Security Scan Results
⏱️ SLA Breach Summary
✅ BUILD PASSED - All security checks passed |
…age gap
- Fix misleading comment: only the retryCondition composition avoids
mutation; config.host is still mutated elsewhere in the function
- Emit a structured logHandler payload ({ type, message, error }) when
retryCondition throws, consistent with request/response interceptors
- Add !error.response guard to isTransientNetworkError so it only retries
errors that arrived before any HTTP response, matching the JSDoc contract
- Add tests (h1, h, k) to cover the ?? fallback branch, the logHandler?.
undefined branch, and the user retryCondition returning true; branch
coverage on contentstack.ts: 93.88% → 97.95%
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔒 Security Scan Results
⏱️ SLA Breach Summary
✅ BUILD PASSED - All security checks passed |
There was a problem hiding this comment.
🟡 Changes recommended
The CHANGELOG entry is missing its release date and the new utils JSDoc text has inaccuracies that should be corrected before merging/releasing.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Suppressed comments (3)
CHANGELOG.md:2
- The Version 5.6.0 changelog entry leaves the Date field blank, but other entries consistently include a formatted date (e.g. "#### Date: Aug-03-2026"). Please fill this in so releases are traceable.
#### Date:
src/common/utils.ts:41
- The
@returnsdescription only mentions matchingerror.code, but the implementation also requires that there is no HTTP response (!error.response). Updating the JSDoc will keep the contract accurate for readers.
* @returns {boolean} True if `error.code` matches a known transient network error code
src/common/utils.ts:39
- This JSDoc sentence is grammatically inverted: it currently reads as if the caller would not be retried ("instead of being silently retried"), which contradicts the function’s purpose and the PR description. Reword so it clearly states the error is retried rather than failing immediately.
* Determines whether an error represents a transient, retryable network-layer
* failure (e.g. DNS lookup failure, connection reset), used to build the SDK's
* default retry behavior so a single blip doesn't crash the caller (e.g. a
* Next.js static build) instead of being silently retried.
- Files reviewed: 5/5 changed files
- Comments generated: 0 new
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
…oc accuracy - Add release date (Aug-05-2026) to the 5.6.0 CHANGELOG entry - Update @returns JSDoc on isTransientNetworkError to reflect both conditions: error.code match and absence of error.response Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔒 Security Scan Results
⏱️ SLA Breach Summary
✅ BUILD PASSED - All security checks passed |
There was a problem hiding this comment.
🟡 Changes recommended
The changelog entry contains a future release date and wording that conflicts with earlier changelog notes, and the new JSDoc text in utils is currently misleading.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Suppressed comments (2)
CHANGELOG.md:4
- The 5.6.0 changelog text reads like transient network errors are being retried for the first time, but 5.5.1 already documents default retry of transient network-level errors. Consider rewording this entry to clarify what specifically changed in 5.6.0 (e.g., composition with user-supplied retryCondition / behavior when retryCondition is present).
Fix: Transient network-layer errors (ENOTFOUND, ENETUNREACH, ECONNRESET, ECONNREFUSED, EAI_AGAIN, ETIMEDOUT, EHOSTUNREACH, ENETDOWN) are now retried automatically using the SDK's configured retry policy instead of failing immediately.
Enhancement: User-supplied `retryCondition` is composed with the default network-error retry logic — both are honoured without either replacing the other. If `retryCondition` throws, the SDK logs a warning via `logHandler` and falls back to default retry behaviour.
src/common/utils.ts:42
- This JSDoc sentence is currently confusing: it implies the caller crashes "instead of being silently retried", but this helper exists to enable retries. Reword to reflect that transient network failures are retried according to policy to avoid crashing callers (e.g., build steps).
* Determines whether an error represents a transient, retryable network-layer
* failure (e.g. DNS lookup failure, connection reset), used to build the SDK's
* default retry behavior so a single blip doesn't crash the caller (e.g. a
* Next.js static build) instead of being silently retried.
* @param {any} error - The error thrown by the underlying HTTP client (Axios)
* @returns {boolean} True if `error.code` matches a known transient network error code and no HTTP response was received (`error.response` is absent)
*/
- Files reviewed: 5/5 changed files
- Comments generated: 0 new
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
Summary
ENOTFOUND,ENETUNREACH,ECONNRESET,ECONNREFUSED,EAI_AGAIN,ETIMEDOUT,EHOSTUNREACH,ENETDOWN) are now retried using the SDK's configured retry policy instead of failing immediately.combinedRetryConditioncomposes any user-suppliedretryConditionwith the new default network-error check — the user condition runs first, the original config is never mutated.retryConditionthrows, a warning is emitted vialogHandlerand the SDK falls back to default retry behaviour.ECONNABORTEDis intentionally excluded —@contentstack/coreclassifies it as a structuredTIMEOUTerror.5.5.0→5.6.0(minor — crux retry logic changed).Test coverage
8 unit tests added covering: ENOTFOUND retry, ENETUNREACH/ETIMEDOUT (customer-reported codes), EAI_AGAIN, ECONNRESET composition with user retryCondition, ECONNABORTED exclusion,
retryOnError: false, andretryLimit: 0.🤖 Generated with Claude Code